<?php
/**
 * Flight: An extensible micro-framework.
 *
 * @copyright   Copyright (c) 2011, Mike Cao <mike@mikecao.com>
 * @license     MIT, http://flightphp.com/license
 */

namespace framework\template;
use framework\core\Cache as Cache;
use framework\Flight;

class View {

    /**
     * 模板赋值
     * @var array
     */
    protected $vars = [];

    /**
     * 模板文件
     * @var string
     */
    private $template;

    /**
     * 模板配置
     * @var array
     */
    protected $config = [];

    /**
     * 缓存对象
     * @var null
     */
    protected $cache = null;


    /**
     * 初始化类
     * @param string $path 模板路径
     */
    public function __construct($config = []) {
        $this->config = Flight::get('TPL');
        $this->config = array_merge($this->config, $config);
        $this->cache = new cache($this->config['TPL_CACHE']);
        $this->set('__Template', $this);

    }

    /**
     * 获取模板赋值
     * @param string $key 键名
     * @return mixed Value
     */
    public function get($key) {
        return isset($this->vars[$key]) ? $this->vars[$key] : null;
    }

    /**
     * 设置模板赋值
     * @param mixed $key 键名
     * @param string $value 键值
     */
    public function set($key, $value = null) {
        if (is_array($key) || is_object($key)) {
            foreach ($key as $k => $v) {
                $this->vars[$k] = $v;
            }
        } else {
            $this->vars[$key] = $value;
        }
    }

    /**
     * 检查赋值存在
     * @param string $key 键名
     * @return boolean
     */
    public function has($key) {
        return isset($this->vars[$key]);
    }

    /**
     * 清除模板赋值
     * @param string $key 键名
     */
    public function clear($key = null) {
        if (is_null($key)) {
            $this->vars = [];
        } else {
            unset($this->vars[$key]);
        }
    }


    /**
     * 模板输出显示
     * @param string $file 模板文件名
     */
    public function render($file, $data = null) {
        $this->exists($file);
        $template = $this->compile($this->template, $data);
        extract($this->vars);
        eval('?>' . $template);
    }

    /**
     * 获取渲染模板内容
     * @param string $file 模板文件名
     * @param array $data 赋值数据
     * @return string 模板内容
     */
    public function fetch($file, $data = null) {
        ob_start();
        $this->render($file, $data);
        return ob_get_clean();
    }

    /**
     * 检查模板存在
     * @param $file
     * @throws \Exception
     */
    public function exists($file) {
        $this->template = $this->getTemplate($file);
        if (!file_exists($this->template)) {
            throw new \Exception("Template file not found: {$this->template}.");
        }
    }

    /**
     * 获取模板路径
     * @param string $file 模板文件名
     * @return string 完整模板路径
     */
    public function getTemplate($file) {
        $ext = $this->extension;
        if (!empty($ext) && (substr($file, -1 * strlen($ext)) != $ext)) {
            $file .= $ext;
        }
       
        if ((substr($file, 0, 1) == '/')) {
            return $file;
        }
        return $file;
    }

    /**
     * HTML还原
     * @param string $str 字符串
     * @return string html内容
     */
    public function e($str) {
        echo htmlentities($str);
    }

    /**
     * 模板编译
     * @param $filePath
     * @param null $data
     * @return string
     */
    public function compile($filePath, $data = null) {
        if (is_array($data)) {
            $this->vars = array_merge($this->vars, $data);
        }
        $fileName = 'tpl.' . md5($filePath);
        $fileTime = filemtime($filePath);
        $cache = $this->cache->get($fileName);
        $cache = json_decode($cache, true);
        if (empty($cache) || $fileTime > $cache['time']) {
            $template = file_get_contents($filePath);
            $template = $this->templateParse($template);
            $ret = array('template'=>$template, 'compile_time'=>time());
            $this->cache->set($fileName, serialize($ret), 86400*365);
        } else {
            $template = $cache['tpl'];
        }
        return $template;
    }

    /**
     * 设置调用外部方法
     */
    public function getFunction($api,$param = array()){
        $str      = explode('/', $api,3);
        $app      = strtolower($str[0]);
        $api      = ucfirst(strtolower($str[1]));
        $function = ucfirst(strtolower($str[2]));
        $ApiClass = "{$app}/{$api}";
        return Flight::api($ApiClass)->$function($param);
    }

    /**
     * 设置外部扩展标签
     */
    public function setExtTags($extlabel) {
       $this->extlabel = $extlabel;
    }

    /**
     * 设置系统标签
     */
    public function setTags() {     
        $this->label = array(         
            /**variable label
                {$name} => <?php echo $name;?>
                {$user['name']} => <?php echo $user['name'];?>
                {$user.name}    => <?php echo $user['name'];?>
            */  

            '/\$(\w+)\.(\w+)\.(\w+)\.(\w+)/is' => "\$\\1['\\2']['\\3']['\\4']",
            '/\$(\w+)\.(\w+)\.(\w+)/is' => "\$\\1['\\2']['\\3']",
            '/\$(\w+)\.(\w+)/is' => "\$\\1['\\2']",  
            '/{(\\$[a-zA-Z_]\w*(?:\[[\w\.\"\'\[\]\$]+\])*)}/i' => "<?php echo $1; ?>",

            /**constance label
            {CONSTANCE} => <?php echo CONSTANCE;?>
            */
            '/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s' => "<?php echo \\1;?>",
                 
            /**if label
                {if $name==1}       =>  <?php if ($name==1){ ?>
                {elseif $name==2}   =>  <?php } elseif ($name==2){ ?>
                {else}              =>  <?php } else { ?>
                {/if}               =>  <?php } ?>
            */              
            '/\{if\s+(.+?)\}/' => "<?php if(\\1) { ?>",
            '/\{else\}/' => "<?php } else { ?>",
            '/\{elseif\s+(.+?)\}/' => "<?php } elseif (\\1) { ?>",
            '/\{\/if\}/' => "<?php } ?>",
            
            /**for label
                {for $i=0;$i<10;$i++}   =>  <?php for($i=0;$i<10;$i++) { ?>
                {/for}                  =>  <?php } ?>
            */              
            '/\{for\s+(.+?)\}/' => "<?php for(\\1) { ?>",
            '/\{\/for\}/' => "<?php } ?>",
            
            /**foreach label
                {foreach $arr as $vo}           =>  <?php $n=1; if (is_array($arr) foreach($arr as $vo){ ?>
                {foreach $arr as $key => $vo}   =>  <?php $n=1; if (is_array($array) foreach($arr as $key => $vo){ ?>
                {/foreach}                  =>  <?php $n++;}unset($n) ?> 
            */
            '/\{foreach\s+(\S+)\s+as\s+(\S+)\}/' => "<?php \$n=1;if(is_array(\\1)) foreach(\\1 as \\2) { ?>", 
            '/\{foreach\s+(\S+)\s+as\s+(\S+)\s*=>\s*(\S+)\}/' => "<?php \$n=1; if(is_array(\\1)) foreach(\\1 as \\2 => \\3) { ?>",
            '/\{\/foreach\}/' => "<?php \$n++;}unset(\$n); ?>",
            
            /*
                {loop $arr $vo}         =>  <?php $n=1; if (is_array($arr) foreach($arr as $vo){ ?>
                {loop $arr $key $vo}    =>  <?php $n=1; if (is_array($array) foreach($arr as $key => $vo){ ?>
                {/loop}                 =>  <?php $n++;}unset($n) ?>
             */
            '/\{loop\s+(\S+)\s+(\S+)\}/' => "<?php \$n=1;if(is_array(\\1)) foreach(\\1 AS \\2) { ?>",
            '/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/' => "<?php \$n=1; if(is_array(\\1)) foreach(\\1 AS \\2 => \\3) { ?>",
            '/\{\/loop\}/' => "<?php \$n++;}unset(\$n); ?>",

            /**function label
                {date('Y-m-d H:i:s')}   =>  <?php echo date('Y-m-d H:i:s');?> 
                {$date('Y-m-d H:i:s')}  =>  <?php echo $date('Y-m-d H:i:s');?> 
            */
            '/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/' => "<?php echo \\1;?>",
            '/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/' => "<?php echo \\1;?>", 
        );
        if(!empty($this->extlabel)){
            $this->label = array_merge($this->label,$this->extlabel);
        }
    }


    /**
     * 模板解析
     * @param  string $template 模板内容
     * @return string
     */
    public function templateParse($template) {
        $this->setTags();
        $template = str_replace('{__PUBLIC__}', __PUBLIC__, $template);
        $template = str_replace('{__URL__}', __URL__, $template);
        foreach($this->label as $key => $vo) {
            $template = preg_replace($key,$vo,$template);
        }
        return trim($template);
    }
}